using System;
using SimpleServiceBus.Bus.MessageManagement.Callbacks;

namespace SimpleServiceBus.Bus.MessageManagement.Callbacks
{
    public class Callback : ICallback
    {
        private readonly string messageId;
        private BusAsyncResult asyncResult = null;
        private CompletionResult completionResult = null;


        public Callback(string messageId)
        {
            this.messageId = messageId;
        }

        internal Callback(string messageId, ICorrelatedCallbackManager correlatedCallbackManager)
        {
            this.messageId = messageId;
        }

        public event EventHandler<BusAsyncResultEventArgs> Registered;

        public string MessageId
        {
            get { return messageId; }
        }

        #region ICallback Members

        public IAsyncResult Register(AsyncCallback callback, object state)
        {
            lock (messageId)
            {
                if (asyncResult != null)
                    throw new ArgumentException("You can only register once");

                asyncResult = new BusAsyncResult(callback, state);

                if (this.Registered != null)
                    this.Registered(this, new BusAsyncResultEventArgs(asyncResult, messageId));
            }

            return asyncResult;
        }

        public CompletionResult WaitForResponse(TimeSpan timeout)
        {
            lock (messageId)
            {
                if (completionResult != null)
                    return completionResult;
            }

            IAsyncResult result = Register(null, null);
            result.AsyncWaitHandle.WaitOne(timeout, false);
            asyncResult = null;
            return result.AsyncState as CompletionResult;
        }

        public string CorrelationID
        {
            get { return messageId; }
        }

        #endregion

        internal void Complete(int errorCode, object message)
        {
            BusAsyncResult result = null;
            lock (messageId)
            {
                result = asyncResult;
                if(result == null)
                    completionResult = new CompletionResult
                    {
                        ErrorCode = errorCode,
                        Message = message
                    };
            }
            if (result != null)
                result.Complete(errorCode, message);
        }
    }

    public class BusAsyncResultEventArgs : EventArgs
    {
        private readonly BusAsyncResult result;
        private readonly string messageId;
        
        public BusAsyncResultEventArgs(BusAsyncResult result, string messageId)
        {
            this.result = result;
            this.messageId = messageId;
        }

        public BusAsyncResult Result
        {
            get
            {
                return result;
            }
        }

        public string MessageId
        {
            get { return messageId; }
        }
    }

    public class UnregisterEventArgs : EventArgs
    {
        private readonly string messageId;

        public UnregisterEventArgs(string messageId)
        {
            this.messageId = messageId;
        }

        public string MessageId
        {
            get { return messageId; }
        }
    }
}
